# --------------------------------------------------------------------------------------------------------------------
# Project:             Digital Payment System (DPS)
# Website:             http://code.google.com/p/dps-x509/
# Purpose of document: This class represents the attribute and value fields within the x509 certificates
# --------------------------------------------------------------------------------------------------------------------
import sys, string, logging

sys.path.append("..")

from pyasn1.type import univ

try:
  import keyczar.keys as keys
except ImportError:
  from keyczar import *
  
import dps.asn1CertificateSpecification
import dps.errors
import dps.utils
import dps.x509Helper

# --------------------------------------------------------------------------------------------------------------------
def getExtensionOidMap():
  """ 
  these are arbitary assignments of extension to values. these would need to be purchased from itu
  if we were doing it properly at http://www.itu.int/en/pages/default.aspx
  @return: oid -> string representation dict
  @rtype:  dict 
  """
  return {
   "2.5.4.5000.1": "tokenType",
   "2.5.4.5000.2": "tokenCode",
   "2.5.4.5000.3": "tokenQuantity",
   "2.5.4.5000.4": "redeemer",
   "2.5.4.5000.5": "exchanger",
   "2.5.4.5000.6": "registrar",
   "2.5.4.5000.7": "id",
   "2.5.4.5000.8": "posInChain",
  }

# --------------------------------------------------------------------------------------------------------------------
def canFindExtensionOid(oid):
  """ 
  converts a string to its key (eg. 2.5.4.7)
  @param   oid: dot notation oid string
  @type    oid: string
  @return: is oid in extension dictionary?
  @rtype:  bool
  """
  return getExtensionOidMap().has_key(oid)

# --------------------------------------------------------------------------------------------------------------------
def extensionOidToStringRepresentation(oid):
  """ 
  converts a key (eg. 2.5.4.7) to its string representation 
  @param   oid: dot notation oid string
  @type    oid: string
  @return: no match return oid 
  @rtype:  bool
  """
  ret = oid
  ismatch = canFindExtensionOid(oid)
  if ismatch:
    ret = getExtensionOidMap()[oid]
  dps.utils.out("x509Extension.canFindExtensionOid: oid: %s str: %s isMatch: %s" % (oid, ret, ismatch), 1)
  return ret

# --------------------------------------------------------------------------------------------------------------------
def stringRepresentationToExtensionOid(s):
  """ 
  converts a string to its key (eg. 2.5.4.7) 
  @param   s: display format string
  @type    s: string
  @return: oid dot representation string
  @rtype:  string
  """
  oid = s
  oids = getExtensionOidMap()
  ucs = s.upper()
  for k in oids.keys():
    if oids[k].upper() == ucs:
      oid = k
      break
  dps.utils.out("x509Extension.stringRepresentationToExtensionOid: str: %s oid: %s" % (s, str(oid)), 1)        
  return oid


# --------------------------------------------------------------------------------------------------------------------
class X509Extension:
  """ This class represents the extention field within the x509 certificates """ 

# --------------------------------------------------------------------------------------------------------------------
  def __init__(self, value, oidKeyStr, isRequired= True):
    """ 
    constructor 
    @param  oidKeyStr: string as key. ie. 2.5.4.7. http://en.wikipedia.org/wiki/Object_identifier
    @type   oidKeyStr: string
    @param  value: representation of item
    @type   value: string
    @param  isRequired: is the extension required
    @type   isRequired: bool 
    """    
    self.__oid_ = oidKeyStr
    self.__value_ = value
    self.__isRequired_ = isRequired
  
# --------------------------------------------------------------------------------------------------------------------
  @staticmethod  
  def loadFromAsn1(asn1Obj):
    """ 
    create from an asn1 object
    @param   asn1Obj: certificate
    @type    asn1Obj: dps.asn1CertificateSpecification.Extension
    @return: extension object
    @rtype:  X509Extension
    """
    dps.utils.assertion(isinstance(asn1Obj, dps.asn1CertificateSpecification.Extension), \
                        "x509Extension.loadFromAsn1: Invalid asn1Obj")

    oid = asn1Obj.getComponentByPosition(0)
    isReq = asn1Obj.getComponentByPosition(1)
    valOct = asn1Obj.getComponentByPosition(2)
    
    return X509Extension(str(valOct), oid, isReq)
    
# --------------------------------------------------------------------------------------------------------------------
  def asAsn1(self):
    """ 
    return object as asn1
    @rtype: asn1 
    """
    ext = dps.asn1CertificateSpecification.Extension()
    
    oid = univ.ObjectIdentifier(self.__oid_)
    isRequired = univ.Boolean(self.__isRequired_)
    octStr = univ.OctetString(self.__value_)
    
    ext.setComponentByPosition(0, oid)
    ext.setComponentByPosition(1, isRequired)
    ext.setComponentByPosition(2, octStr)
    
    return ext

# --------------------------------------------------------------------------------------------------------------------
  def getOidStringRepresentation(self):
    """ 
    return object as type 
    @rtype: string
    """
    return extensionOidToStringRepresentation(self.__oid_)

# --------------------------------------------------------------------------------------------------------------------
  def getOid(self):
    """ 
    return object as type
    @rtype: univ.ObjectIdentifier
    """
    return self.__oid_

# --------------------------------------------------------------------------------------------------------------------
  def getValue(self):
    """ 
    return object as asn1 
    @rtype: univ string type 
    """
    return self.__value_
  
# --------------------------------------------------------------------------------------------------------------------
  def __str__(self):
    """ as string """
    return "v: %s oid: (%s) %s bool: %s" % \
      (self.__value_, self.getOidStringRepresentation(), self.__oid_, self.__isRequired_)    

  
# --------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------------------
class X509ExtensionList:
  """ array of x509Extensions """
  
# --------------------------------------------------------------------------------------------------------------------
  def __init__(self, cloneFromAsn1= None):
    """ 
    constructor
    @param cloneFromAsn1: dps.asn1CertificateSpecification.Extensions to clone it from 
    @type  cloneFromAsn1: dps.asn1CertificateSpecification.Extensions 
    """
    dps.utils.assertion(isinstance(cloneFromAsn1, dps.asn1CertificateSpecification.Extensions), \
                        "x509ExtensionList.__init__: Invalid cloneFromAsn1")
    self.__list_ = []
    self.__clone_ = cloneFromAsn1

# --------------------------------------------------------------------------------------------------------------------
  @staticmethod  
  def loadFromAsn1(asn1Obj):
    """ 
    create from an asn1 object
    @param asn1Obj: certificate
    @type asn1Obj: dps.asn1CertificateSpecification.Extensions
    @rtype: X509ExtensionList
    """

    dps.utils.assertion(isinstance(asn1Obj, dps.asn1CertificateSpecification.Extensions), \
                        "x509ExtensionList.loadFromAsn1: Invalid asn1Obj")
    avlist = X509ExtensionList(asn1Obj)
    try:
      for s in asn1Obj:
        av = X509Extension.loadFromAsn1(s)
        avlist.addItem(av)
    except dps.errors.DPSBaseError:
      pass #raise?
    except Exception, e:
      dps.utils.out("Exception in x509ExtensionList.loadFromAsn1: " + str(sys.exc_info()[0]) + str(e))
      #raise dps.errors.DecodingError()
    #dps.utils.out("x509ExtensionList.loadFromAsn1: " + str(len(avlist)))  
    
    return avlist
  
# --------------------------------------------------------------------------------------------------------------------
  def addItem(self, asn1Obj):
    """ 
    add item to the list
    @param asn1Obj: item to add
    @rtype: dps.x509Extension.x509Extension 
    """
    dps.utils.assertion(isinstance(asn1Obj, X509Extension), \
                        "x509ExtensionList.addItem: Invalid asn1Obj")
    self.__list_.append(asn1Obj)

# --------------------------------------------------------------------------------------------------------------------
  def asAsn1(self):
    """ 
    @return: the list as an asn1
    @rtype:  dps.asn1CertificateSpecification.Name
    """
    extensions = None
    if self.__clone_:
      extensions = self.__clone_.clone()
    else:
      extensions = dps.asn1CertificateSpecification.Extensions()
 
    for i in range(len(self.__list_)):
      ext = self.__list_[i].asAsn1()
      extensions.setComponentByPosition(i, ext)
 
    return extensions

# --------------------------------------------------------------------------------------------------------------------
  def __getitem__(self, oidKeyStr):
    """ get[] operator 
    @param  oidKeyStr: the key for the wanted item
    @type   oidKeyStr: string
    @rtype: dps.X509Extension if found or None """
    oid = univ.ObjectIdentifier(oidKeyStr)
    
    item = None
    for i in self.__list_:
      if i.getOid() == oid:
        item = i
        break
    return item
  
# --------------------------------------------------------------------------------------------------------------------
  def __str__(self):
    """ as string """
    ret = ""
    for l in self.__list_:
      ret += str(l) + " "
    ret = ret[:-1]
    return ret
